#include <iostream>
#include <sstream>
#include <fltk/Window.h>
#include <fltk/Button.h>
#include <fltk/Monitor.h>

#include <profo/Protein.h>
#include <profo/HP2DRectangularView.h>
#include <profo/SimpleHP2DRectangularAlgorithm.h>
#include <profo/util.h>

using namespace profo;

/**
 * A callback that is called when the view is closed.
 */
void HP2DRectangularView::closeCb(fltk::Widget* w, void* data) {
  HP2DRectangularView* obj = reinterpret_cast<HP2DRectangularView*>(data);
  obj->getProtein().clear(); // when we close the view we also clear the protein.
  w->hide();
} // closeCb() method

/**
 * ATTENTION: neButton is actually used to move to SOUTH-EAST direction!
 * It is so because Y axis is turned downwards in the view!
 */
void HP2DRectangularView::neButtonCb(fltk::Button* w, void* data) {
  unsigned char myMove = 7;
  HP2DRectangularView* obj = reinterpret_cast<HP2DRectangularView*>(data);
  obj->getAlgorithm().move(myMove);
  obj->_energyLabel->copy_label(obj->getProtein().energyString().c_str());
  obj->_energyLabel->redraw();
  AminoAcid& aa = obj->getProtein().get(obj->getProtein().selected());
  obj->updateControlButtons(aa.free);
  obj->updateView();
#ifdef DEBUG
  std::cout << obj->getProtein().get(0).toString() << std::endl;
  std::cout << "protein: " << obj->getProtein().toString() << std::endl;
  std::cout << "selected ID: " << obj->getProtein().selected() << std::endl;
#endif
} // neButtonCb() method

/**
 * This method is called whenever update of control buttons is needed.
 * @param arg A byte with "free move" bits set
 */
void HP2DRectangularView::updateControlButtons(unsigned char arg) {
  // east direction
  if (getBit(0, arg))
    eButton->activate(true);
  else
    eButton->deactivate();

  // north-east direction
  if (getBit(7, arg))
    neButton->activate(true);
  else
    neButton->deactivate();

  // north direction
  if (getBit(6, arg))
    nButton->activate(true);
  else
    nButton->deactivate();

  // north-west direction
  if (getBit(5, arg))
    nwButton->activate(true);
  else
    nwButton->deactivate();

  // west direction
  if (getBit(4, arg))
    wButton->activate(true);
  else
    wButton->deactivate();

  // south-west direction
  if (getBit(3, arg))
    swButton->activate(true);
  else
    swButton->deactivate();

  // south direction
  if (getBit(2, arg))
    sButton->activate(true);
  else
    sButton->deactivate();

  // south-east direction
  if (getBit(1, arg))
    seButton->activate(true);
  else
    seButton->deactivate();
} //

/**
 * An implementation of the View's virtual updateView() method.
 */
void HP2DRectangularView::updateView() {
  _proteinView->update();
} // updateView() method

/**
 * A (static) callback method to handle parsing input protein string event.
 */
void HP2DRectangularView::parseButtonCb(fltk::Button* w, void* data) {
  HP2DRectangularView* obj = reinterpret_cast<HP2DRectangularView*>(data);
  std::cout << "input: " << obj->_input->text() << std::endl;
  obj->getAlgorithm().initProtein(obj->_input->text());
  obj->getAlgorithm().updateFreeMoves();
  obj->_energyLabel->copy_label(obj->getProtein().energyString().c_str());
  obj->_energyLabel->redraw();
  obj->updateView();
#ifdef DEBUG
  std::cout << "protein: " << obj->getProtein().toString() << std::endl;
  std::cout << "energy: " << obj->getProtein().energyString() << std::endl;
  // run SimpleHP2DRectangularAlgorithm::unittest()
  SimpleHP2DRectangularAlgorithm::unittest();
#endif
} // parseButtonCb() method

HP2DRectangularView::HP2DRectangularView(Protein& argProtein, IAlgorithm& argAlgorithm):
  _proteinModelRef(argProtein),
  _algorithmRef(argAlgorithm)
{
  fltk::Window* w;
  {fltk::Window* o = _window = new fltk::Window(600, 500, "PROFO: H-P 2D Rectangular View");
    w = o;
    o->callback(closeCb, (void*)(this));
    o->user_data((void*)(this));
    // put our window in the center of the screen.
    o->Rectangle::set(fltk::Monitor::find(0,0),o->w(),o->h(),fltk::ALIGN_CENTER);
    o->size_range(320,200); // Do not allow user to shrink window more than this
    o->begin();
      {ProteinView2D* o = _proteinView = new ProteinView2D(_proteinModelRef, 10, 10, 580, 385);
      _proteinView->mainViewPtr = this;
      _window->resizable(o);
      o->box(fltk::THIN_DOWN_BOX);
      o->end();
      }
     {fltk::Group* o = _controlPanel = new fltk::Group(10, 405, 580, 85);
      o->box(fltk::THIN_DOWN_BOX);
      o->begin();
      {fltk::Button* o = nwButton = new fltk::Button(500, 5, 25, 25, "@135->");
        o->tooltip("Move selected protein in NW direction.");
        o->deactivate();
      }
      {fltk::Button* o = nButton = new fltk::Button(525, 5, 25, 25, "@90->");
        o->tooltip("Move selected protein in N direction.");
        o->deactivate();
      }
      {fltk::Button* o = neButton = new fltk::Button(550, 5, 25, 25, "@45->");
        o->callback((fltk::Callback*)neButtonCb, (void*)(this));
        o->tooltip("Move selected protein in NE direction.");
        o->deactivate();
      }
       {fltk::Button* o = wButton = new fltk::Button(500, 30, 25, 25, "@<-;");
        o->tooltip("Move selected protein in W direction.");
        o->deactivate();
      }
       {fltk::Button* o = eButton = new fltk::Button(550, 30, 25, 25, "@->");
        o->tooltip("Move selected protein in E direction.");
        o->deactivate();
      }
       {fltk::Button* o = swButton = new fltk::Button(500, 55, 25, 25, "@225->");
        o->tooltip("Move selected protein in SW direction.");
        o->deactivate();
      }
       {fltk::Button* o = sButton = new fltk::Button(525, 55, 25, 25, "@270->");
        o->tooltip("Move selected protein in S direction.");
        o->deactivate();
      }
       {fltk::Button* o = seButton = new fltk::Button(550, 55, 25, 25, "@315->");
        o->tooltip("Move selected protein in SE direction.");
        o->deactivate();
      }
      // Input protein sequence string
      {fltk::Input* o = _input = new fltk::Input(5, 5, 400, 25);
#ifdef DEBUG
        _input->text("hhhpppphhpphh");
#endif
        fltk::Group::current()->resizable(o);
      }
      // Parse button
      {fltk::Button* o = parseButton = new fltk::Button(405, 5, 25, 25, "@[]<");
        // we set a callback function for this widget, and, for convenience,
        // we pass a pointer to the fltk::Input object that holds the string
        // as user-defined data.
        o->callback((fltk::Callback*)parseButtonCb, (void*)(this));
        o->tooltip("Click here to parse the input string.");
      }
       {fltk::Widget* o = _algorithmInfo = new fltk::Widget(5, 35, 405, 25, "Algorithm information");
        // set the label to a good description of the algorithm.
        o->copy_label(_proteinModelRef.description().c_str());
        o->box(fltk::NO_BOX);
        o->buttonbox(fltk::NO_BOX);
        o->align(fltk::ALIGN_LEFT|fltk::ALIGN_INSIDE);
      }
       {fltk::Widget* o = _energyLabel = new fltk::Widget(5, 55, 405, 25, "Energy: 0");
        o->copy_label(_proteinModelRef.energyString().c_str());
        o->box(fltk::NO_BOX);
        o->buttonbox(fltk::NO_BOX);
        o->align(fltk::ALIGN_LEFT|fltk::ALIGN_INSIDE);
      }
       {fltk::Button* o = new fltk::Button(435, 5, 60, 25, "&Start");
        o->shortcut(0x80073);
        o->tooltip("Press here to start/continue the simulation.");
        o->deactivate();
      }
       {fltk::Button* o = new fltk::Button(435, 30, 60, 25, "Sto&p");
        o->shortcut(0x80070);
        o->tooltip("Press here to stop the running algorithm.");
        o->deactivate();
      }
      o->end();
    }
    o->end();
  }
}

void HP2DRectangularView::show() {
  _window->show();
}

// $Id$
